using PBO.Data;
using PBO.Game.BattleLog;
using PokemonBattle.BattleNetwork;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace PBO.Game.BattleClass
{
    public partial class Battle
    {
        public ILogBuilder log { get; private set; }

        public Battle(ILogBuilder textValue, BattleMode mode)
        {
            Group1 = new BattleGroup(this);
            Group2 = new BattleGroup(this);
            log = textValue;
            this.Mode = mode;
            _waitPlayerCount = this.Mode == BattleMode.Double_4P ? 4 : 2;
        }

        private int _waitPlayerCount;
        public void SetPlayer(byte position, string identity)
        {
            _players[position] = identity;
            _waitPlayerCount--;
        }

        public void SetTeams(Dictionary<byte, TeamData> teams)
        {
            foreach (byte key in teams.Keys)
            {
                _teamDatas[key] = teams[key];
            }
        }

        public bool PlayerReady
        {
            get { return _waitPlayerCount == 0; }
        }

        public void InitializeBattle()
        {
            _random = new Random(this.RandomSeed);
            if (this.Rules.Contains(BattleRule.Random))
            {
                for (byte i = 1; i <= _teamDatas.Keys.Count; i++)
                {
                    _teamDatas[i] = GameService.GetRandomTeam(_random);
                }
            }
            this.Ground = new BattleGround(this.GetRandom(BattleGround.TERRAINS), this);
            InitializeGroup();
            this.Replay = new BattleReplay(_teamDatas, this.RandomSeed, _players, this.Mode, this.Rules);
            this.BattleReady = true;
        }

        public TurnResult StartBattle()
        {
            log.AppendMsg("GameStart");
            if (Convert.ToBoolean(Rules.Contains(BattleRule.PPUp)))
            {
                log.AppendMsg("GameRule", "PP+");
            }

            if (Convert.ToBoolean(Rules.Contains(BattleRule.Random)))
            {
                log.AppendMsg("GameRule", "Random(随机)");
            }
            ShowPokemon();
            log.AppendLog(string.Empty);
            waitMoveCount = Mode == BattleMode.Single ? 2 : 4;
            return new TurnResult(true);
        }

        private void ShowPokemon()
        {
            var pokemons = this.SelectedPokemons.ToList();

            foreach (Pokemon pm in pokemons)
            {
                log.AppendMsg("SendOut1", pm.MyTeam.PlayerName, pm.NickName + "(" + pm.NameBase + ")");
                //log.AddText(pm.MyTeam.GetPlayerName(pm) + "放出了" + pm.NickName + "(" + pm.NameBase + ")！");
            }

            pokemons.Sort((p1, p2) => p2.Speed.CompareTo(p1.Speed));

            foreach (Pokemon pm in pokemons)
            {
                pm.Showed();
            }
        }

        private List<PlayerBattleMove> changeList = new List<PlayerBattleMove>();
        private List<PlayerBattleMove> atkList = new List<PlayerBattleMove>();

        private int lastMoveId = 0;
        private int waitMoveCount;
        private BattleTurnMove nextTurnMove = BattleTurnMove.NextTurn;

        private List<PlayerMove> moveList = new List<PlayerMove>();
        public void SetMove(PlayerMove move)
        {
            moveList.Add(move);
            waitMoveCount--;
        }
        public bool MoveReady
        {
            get { return waitMoveCount == 0; }
        }

        private readonly static PokemonIndex[] POKEMON_INDEX = { PokemonIndex.Pokemon1OfTeam1, PokemonIndex.Pokemon2OfTeam1, PokemonIndex.Pokemon1OfTeam2, PokemonIndex.Pokemon2OfTeam2 };
        private static int ComparePokemonIndex(PlayerMove move1, PlayerMove move2)
        {
            return Array.IndexOf(POKEMON_INDEX, move1.Pokemon).CompareTo(Array.IndexOf(POKEMON_INDEX, move2.Pokemon));
        }

        public TurnResult NextResult()
        {
            moveList.Sort(ComparePokemonIndex);

            RecordMoves(moveList);

            List<PlayerBattleMove> list = new List<PlayerBattleMove>();
            foreach (PlayerMove move in moveList)
            {
                list.Add(new PlayerBattleMove(move));
            }
            moveList.Clear();

            if (nextTurnMove == BattleTurnMove.Pass)
            {
                return PassChange(list[0]);
            }
            else if (nextTurnMove == BattleTurnMove.Death)
            {
                return DeathChange(list);
            }
            else
            {
                return NextTurn(list);
            }
        }

        private void RecordMoves(IEnumerable<PlayerMove> moves)
        {
            this.Replay.AddMove(moves);
        }

        private TurnResult NextTurn(List<PlayerBattleMove> moves)
        {
            BeginTurn();
            SortPlayerMoves(moves);
            return NextTurn();
        }

        private TurnResult NextTurn()
        {
            while (changeList.Count != 0)
            {
                PlayerBattleMove move = changeList[0];
                changeList.Remove(move);
                PlayerBattleMove pursuit = GetPursuitMove(move.PM, atkList);
                if (pursuit != null)
                {
                    atkList.Remove(pursuit);
                    pursuit.PM.BattleState.pursuit = true;
                    NextMove(pursuit);
                    pursuit.PM.BattleState.pursuit = false;
                    AddLine();
                }
                if (move.PM.HealthPower != 0)
                {
                    move.PM.ChangePM(move, false);
                    AddLine();
                }
            }
            while (atkList.Count != 0)
            {
                PlayerBattleMove move = atkList[0];
                atkList.Remove(move);
                if (move.PM.HealthPower != 0 && move.PM.MyTeam.SelectedPokemon.Contains(move.PM))
                {
                    NextMove(move);
                    if (move.PM.BattleState.pass)
                    {
                        TurnResult result = new TurnResult(false);
                        result.PassPokemon = move.PM;
                        nextTurnMove = BattleTurnMove.Pass;
                        waitMoveCount = 1;
                        return result;
                    }
                    else if (move.PM.BattleState.healwish || move.PM.BattleState.lunarDance)
                    {
                        if (move.PM.CanChange())
                        {
                            TurnResult result = new TurnResult(false);
                            result.DeadPokemons.Add(move.PM);
                            nextTurnMove = BattleTurnMove.Death;
                            waitMoveCount = 1;
                            return result;
                        }
                    }
                    AddLine();
                }
            }

            if (Group1.NoPM || Group2.NoPM)
            {
                GameOver();
            }
            else
            {
                List<Pokemon> deadList = EndTurn();
                if (Group1.NoPM || Group2.NoPM)
                {
                    GameOver();
                }
                else
                {
                    if (deadList.Count > 0)
                    {
                        TurnResult result = new TurnResult(false);
                        result.DeadPokemons.AddRange(deadList);
                        nextTurnMove = BattleTurnMove.Death;
                        waitMoveCount = deadList.Count;
                        return result;
                    }
                }
            }
            nextTurnMove = BattleTurnMove.NextTurn;
            waitMoveCount = Group1.AliveSelectedPokemon.Count() + Group2.AliveSelectedPokemon.Count();
            return new TurnResult(true);
        }

        private bool BringToAttackListFront(Pokemon pm)
        {
            PlayerBattleMove attackMove = atkList.Find(m => object.ReferenceEquals(m.PM, pm));
            if (attackMove != null)
            {
                atkList.Remove(attackMove);
                atkList.Insert(0, attackMove);
                return true;
            }
            else
            {
                return false;
            }
        }

        private bool BringToAttackListBack(Pokemon pm)
        {
            PlayerBattleMove attackMove = atkList.Find(m => object.ReferenceEquals(m.PM, pm));
            if (attackMove != null)
            {
                atkList.Remove(attackMove);
                atkList.Add(attackMove);
                return true;
            }
            else
            {
                return false;
            }
        }

        private TurnResult DeathChange(IEnumerable<PlayerBattleMove> moves)
        {
            foreach (PlayerBattleMove move in moves)
            {
                move.SetPM(this);
                move.PM.ChangePM(move, false);
            }
            return NextTurn();
        }

        private TurnResult PassChange(PlayerBattleMove move)
        {

            move.SetPM(this);
            if (move.PM.BattleState.uTurn)
            {
                PlayerBattleMove pursuit = GetPursuitMove(move.PM, atkList);
                if (pursuit != null && pursuit.PM.HealthPower > 0)
                {
                    atkList.Remove(pursuit);
                    pursuit.PM.BattleState.pursuit = true;
                    NextMove(pursuit);
                    pursuit.PM.BattleState.pursuit = false;
                }
            }
            if (move.PM.HealthPower != 0)
                move.PM.ChangePM(move, true);
            AddLine();
            return NextTurn();
        }

        private PlayerBattleMove GetPursuitMove(Pokemon changePM, IEnumerable<PlayerBattleMove> list)
        {
            return list.FirstOrDefault(atk => atk.Pursuit && atk.GetCurrentTarget(this).Contains(changePM) && !changePM.MyTeam.SelectedPokemon.Contains(atk.PM));
        }

        private void BeginTurn()
        {
            log.AppendText(string.Empty);
            log.AppendMsg("GameResultTie", _turn);
            //log.AppendText("开始回合 #" + _turn);
            BeginTurn(this.SelectedPokemons);
            Ground.followPokemon = null;
            AddLine();
        }

        private bool EndTurnCount;
        private List<Pokemon> EndTurn()
        {
            if (!EndTurnCount)
            {
                Group1.EndTurnCount();
                Group2.EndTurnCount();
                Ground.CountOfTurnEnd(log);
                EndTurnCountPokemon(this.SelectedPokemons);
                EndTurnCount = true;
            }
            List<Pokemon> list = new List<Pokemon>();

            if (Group1.NoPM || Group2.NoPM)
                return list;

            list.AddRange(GetDeathSwapPokemons(Group1));
            list.AddRange(GetDeathSwapPokemons(Group2));

            if (list.Count > 0)
                return list;
            log.AppendMsg("EndTurn", _turn);
            //log.AppendText("结束回合 #" + _turn);

            foreach (var pm in this.SelectedPokemons.Where(p => p.HealthPower > 0))
            {
                log.AppendLog(pm.GetNameString() + "(" + pm.NameBase + "):" + pm.HealthPower + " HP" + pm.State.GetStateString());
            }
            _turn++;
            EndTurnCount = false;
            return list;
        }

        private void EndTurnCountPokemon(IEnumerable<Pokemon> pokemons)
        {
            foreach (Pokemon pm in pokemons)
            {
                if (pm.HealthPower != 0)
                {
                    pm.BattleState.CountOfEndTurn(pm, Ground, log);
                    pm.CountOfEndTurn(_random, this);
                }
            }
        }

        //private IEnumerable<Pokemon> GetDeathSwapPokemons(Team team)
        //{
        //    List<Pokemon> list = new List<Pokemon>();
        //    for (int i = 0; i < team.SelectedPokemon.Count; i++)
        //    {
        //        Pokemon pm = team.SelectedPokemon[i];
        //        if (pm.HealthPower == 0 && team.CanChange(pm) && team.AlivePokemon.Count() > list.Count)
        //        {
        //            list.Add(pm);
        //        }
        //    }
        //    return list;
        //}

        private static IEnumerable<Pokemon> GetDeathSwapPokemons(BattleGroup group)
        {
            List<Pokemon> list = new List<Pokemon>();
            foreach (var pm in group.SelectedPokemon)
            {
                if (pm.HealthPower == 0 && pm.CanChange() && group.AlivePokemonsCount > list.Count)
                {
                    list.Add(pm);
                }
            }
            return list;
        }

        public string GetCaption()
        {
            return this.Group1.PlayerName + " VS " + this.Group2.PlayerName;
        }

        private int CountPokemon()
        {
            int team1count = Group1.AlivePokemonsCount;
            int team2count = Group2.AlivePokemonsCount;
            log.AppendMsg("Chatter", team1count, team2count);
            return team1count.CompareTo(team2count);
        }

        private void GameOver()
        {
            log.AppendText("战斗结束！");
            int result = CountPokemon();

            if (result > 0)
            {
                log.AppendText(Group1.PlayerName + "获得了胜利！");
            }
            else if (result < 0)
            {
                log.AppendText(Group2.PlayerName + "获得了胜利！");
            }
            else
            {
                log.AppendText(Group1.PlayerName + "与" + Group2.PlayerName + "打成了平手！");
            }
            this.Ended = true;
        }

        public void Tie()
        {
            log.AppendText("战斗结束！");
            CountPokemon();
            log.AppendText(Group1.PlayerName + "与" + Group2.PlayerName + "打成了平手！");
            this.Ended = true;
        }

        public void ExitGame(string player)
        {
            log.AppendText(player + "离开了游戏");
            CountPokemon();
            this.Ended = true;
        }

        public List<Pokemon> GetPokemons(int playerIndex)
        {
            var team = this.Teams.FirstOrDefault(t => t.Id == playerIndex);
            if (team == null) return null;

            if (Mode == BattleMode.Double_4P)
            {
                return team.SelectedPokemon.Where(p => p.HealthPower > 0 || p.CanChange()).ToList();
            }
            else
            {
                List<Pokemon> list = new List<Pokemon>();
                foreach (Pokemon pm in team.SelectedPokemon)
                {
                    if (pm.HealthPower > 0 || (pm.CanChange() && list.Count < team.Pokemons.Count(p => p != null && p.HealthPower > 0)))
                        list.Add(pm);
                }
                return list;
            }
        }

        public List<Pokemon> GetTargetsFromIndex(Pokemon pmValue, Move move, TargetIndex targetIndex)
        {
            List<Pokemon> list = new List<Pokemon>();
            var opponentTeam = pmValue.MyTeam.Group.Opponent;

            switch (targetIndex)
            {
                case TargetIndex.DefaultTarget:
                    if (!pmValue.BattleState.bide)
                    {
                        switch (move.Target)
                        {
                            case MoveTarget.二体:
                                return opponentTeam.SelectedPokemon.ToList();
                            case MoveTarget.全体:
                                return SelectedPokemons.Where(p => p != pmValue).ToList();
                            case MoveTarget.己场:
                            case MoveTarget.敌场:
                            case MoveTarget.全场:
                            case MoveTarget.最近:
                            case MoveTarget.技能:
                                break;
                        }
                    }
                    break;
                case TargetIndex.Opponent1:
                    return opponentTeam.SelectedPokemon.Take(1).ToList();
                case TargetIndex.Opponent2:
                    return opponentTeam.SelectedPokemon.Skip(1).Take(1).ToList();
                case TargetIndex.Random:
                    list.Add(GetRandom(opponentTeam.SelectedPokemon));
                    break;
                case TargetIndex.Self:
                    return new List<Pokemon>(new[] { pmValue });
                case TargetIndex.TeamFriend:
                    if (pmValue.Friend != null)
                    {
                        return new List<Pokemon>(new[] { pmValue.Friend });
                    }
                    break;
            }

            return list;
        }

    }
}